<template>
	<div>
		<div id="webgl-content" class="webgl-content"></div>
		<div class="buttons">
			<button @click="changeIndex(1)">显示下一个</button>
			<button @click="$router.push({name:'demo2'})" style="margin-left: 5px;">演示动画模型</button>
		</div>
		<div class="progress" v-if="isLoading"><div class="progress-full" :style="`width:${progress}%;`"></div></div>
	</div>
</template>

<script>
// 方式 1: 导入整个 three.js核心库
import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
import { RoomEnvironment } from 'three/examples/jsm/environments/RoomEnvironment.js';
export default {
	name: 'HelloWorld',
	data() {
		return {
			isLoading: false,
			progress: 0,
			camera: null,
			scene: null,
			renderer: null,
			controls: null,
			theObject: null,
			gltfList: [			
				'robot2.glb',				
				'head.glb',
				'male02.glb',
				'female02.glb',			
			],
			index: 0,
			loaderGLTF: null
		};
	},

	mounted() {
		console.info(['THREE.REVISION -- ', THREE.REVISION]);
		this.init();
		window.addEventListener('resize', this.onWindowResize);		
	},
	beforeDestroy() {
		// 移除绑定的事件监听
		window.removeEventListener('resize', this.onWindowResize);
		try {
			this.removeFromScene(this.theObject);
			this.scene.clear();
			this.renderer.dispose();
			this.renderer.forceContextLoss();
			this.renderer.content = null;
		} catch (e) {
			console.log(e);
		}
	},
	methods: {
		init() {
			// 渲染器
			this.initRenderer();
			// 场景
			this.initScene();
			// 像机
			this.initCamera();
			// 灯光
			this.initLight();
			// 模型加载
			this.loadGLTFModel(this.gltfList[this.index]);
			// 控制
			this.initControls();
			// 动画渲染
			this.animate(); //放在最后
		},

		initRenderer() {
			// 渲染器
			const container = document.getElementById('webgl-content');
			const width = container.clientWidth;
			const height = container.clientHeight;
			this.renderer = new THREE.WebGLRenderer({ antialias: true });
			this.renderer.setSize(width, height);
			this.renderer.setPixelRatio(window.devicePixelRatio);
			this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
			this.renderer.toneMappingExposure = 1;
			this.renderer.outputEncoding = THREE.sRGBEncoding;
			container.appendChild(this.renderer.domElement);
			// console.log("this.renderer.info:",this.renderer.info)
		},

		initScene() {
			// 场景
			this.scene = new THREE.Scene();
			this.scene.background = new THREE.Color(0xbbbbbb); //背景色
			// 辅助网格-可选
			const grid = new THREE.GridHelper(10, 10, 0xffffff, 0xffffff);
			grid.material.opacity = 0.2;
			grid.material.depthWrite = false;
			grid.material.transparent = true;
			this.scene.add(grid);
			//坐标轴辅助-可选
			const axesHelper = new THREE.AxesHelper(5);
			this.scene.add(axesHelper);
			// 场景环境效果-可选
			const environment = new RoomEnvironment();
			const pmremGenerator = new THREE.PMREMGenerator(this.renderer);
			this.scene.environment = pmremGenerator.fromScene(environment).texture;
			environment.dispose();
		},

		initCamera() {
			let size;
			size = this.renderer.getSize(new THREE.Vector2());
			this.camera = new THREE.PerspectiveCamera(45, size.width / size.height, 1, 100);
			this.camera.position.set(2, 2, 10);
			this.camera.lookAt(0, 0, 0);
		},

		initControls() {
			this.controls = null;
			this.controls = new OrbitControls(this.camera, this.renderer.domElement);
			// 如果使用animate方法时，将此函数删除
			//this.controls.addEventListener( 'change', this.renderer );
			// 使动画循环使用时阻尼或自转 意思是否有惯性
			this.controls.enableDamping = true;
			//动态阻尼系数 就是鼠标拖拽旋转灵敏度
			//controls.dampingFactor = 0.25;
			//是否可以缩放
			this.controls.enableZoom = true;
			//是否自动旋转
			// this.controls.autoRotate = true;
			//设置相机距离原点的最远距离
			this.controls.minDistance = 2;
			//设置相机距离原点的最远距离
			this.controls.maxDistance = 60;
			//是否开启右键拖拽
			this.controls.enablePan = true;

			//更新控制器。必须在摄像机的变换发生任何手动改变后调用，
			//或如果.autoRotate或.enableDamping被设置时，在update循环里调用。
			this.controls.update();
		},

		animate() {
			this.controls && this.controls.update();
			this.render();
			requestAnimationFrame(this.animate);
		},

		render() {
			this.renderer.render(this.scene, this.camera);
		},

		// 加载 glTF 模型
		loadGLTFModel(url) {
			// Instantiate a loader			
			if (!this.loaderGLTF) {
				this.loaderGLTF = new GLTFLoader().setPath('/static/models/gltf/');
			}
			this.progress = 0;
			this.isLoading = true;
			this.loaderGLTF.load(
				url, // 模型 URL 可以是本地静态资源，也可以是网上的 url
				gltf => {
					this.isLoading = false;
					// 加载完成后回调
					//console.log(gltf);
					this.removeFromScene(this.theObject);
					this.theObject = gltf.scene;
					this.theObject.name = 'theObject';
					this.scene.add(this.theObject);
					//调整大小，设置居中
					this.setCenter(this.theObject);
				},
				// 加载进度回调
				xhr => {
					//console.log(Math.round((xhr.loaded / xhr.total) * 100) + '% loaded');
					this.progress = Math.round((xhr.loaded / xhr.total) * 100);
				},
				// 加载错误回调
				error => {
					console.error('An error happened：' + error);
					this.isLoading = false;
				}
			);
		},
		initLight() {
			let lightGroup = new THREE.Group();
			lightGroup.name = 'LightGroup';
			let light = new THREE.AmbientLight(0x404040, 5); // 环境光
			lightGroup.add(light);
			// 方向光 类似太阳光
			light = new THREE.DirectionalLight(0xffffff, 3);
			lightGroup.add(light);
			// 点光源 类似白炽灯
			light = new THREE.PointLight(0xff0000, 1, 100);
			light.position.set(25, 25, 25);
			lightGroup.add(light); // 加在场景中
			this.scene.add(lightGroup);			
		},

		onWindowResize() {
			// console.log('onWindowResize....');
			const container = document.getElementById('webgl-content');
			const width = container.clientWidth;
			const height = container.clientHeight;
			this.camera.aspect = width / height;
			this.camera.updateProjectionMatrix();
			this.renderer.setSize(width, height);
			this.render();
		},

		removeFromScene(theObject) {
			if (theObject) {
				// 删除掉所有的模型组内的mesh
				theObject.traverse(item => {
					if (item instanceof THREE.Mesh) {
						item.geometry.dispose(); // 删除几何体
						item.material.dispose(); // 删除材质
						// 删除纹理
						item.material.map && item.material.map.dispose();
						item = null;
					}
				});
				this.scene.remove(theObject);
				theObject = null;
			}
		},
		// 适度调整大小，居中
		setCenter(theObject) {
			let root = theObject; //this.scene;
			let box = new THREE.Box3().setFromObject(root);
			let temp = new THREE.Vector3();
			let center = box.getCenter(new THREE.Vector3());
			let size = box.getSize(new THREE.Vector3());
			// console.log('old center,size:', center, size);
			let maxAxis = Math.max(size.x, size.y, size.z);
			root.scale.multiplyScalar( 5 / maxAxis); //模型显示大小可调整参数 5 
			box.setFromObject(root);
			center = box.getCenter(new THREE.Vector3());
			size = box.getSize(new THREE.Vector3());
			// center = new THREE.Vector3()
			root.position.sub(center); // 位置居中
			// console.log('new center,size:', center, size);
		},

		changeIndex(value) {
			this.index += value;
			if (this.index >= this.gltfList.length) {
				this.index = 0;
			}
			if (this.index < 0) {
				this.index = 0;
			}
			this.loadGLTFModel(this.gltfList[this.index]);			
		}
	}
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.webgl-content * {
	border: 0;
	margin: 0;
	padding: 0;
}
.webgl-content {
	position: absolute;
	top: 50%;
	left: 50%;
	width: 100%;
	height: 100%;
	-webkit-transform: translate(-50%, -50%);
	transform: translate(-50%, -50%);
}

.buttons {
	position: absolute;
	margin: 10px;
	top: 0;
	right: 0;
	display: flex;
	flex-direction: row;
}
.progress {
	position: absolute;
	bottom:10px;
	left: 10%;
	width: 80%;
	height: 18px;
	z-index: 10000;
	background-color: #c5c5c5;
	border: 1px solid #c5c5c5;
	border-radius: 5px;
}
.progress-full {
	background-color: #6c6c6c;
	float: left;
	/* width: 34%; */
	height: 18px;
	display: inline-block;
}
</style>
